package cn.skycity.gateway.config;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.skycity.framework.core.config.IgnoreProperties;
import cn.skycity.framework.core.constants.GlobalConstants;
import cn.skycity.framework.core.constants.SecurityConstants;
import cn.skycity.framework.core.model.ResultCode;
import cn.skycity.framework.core.utils.WebFluxResponseUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

@Component
@Slf4j
@EnableConfigurationProperties(IgnoreProperties.class)
public class GlobalSecurityGlobalFilter implements GlobalFilter, Ordered {
    @Resource
    private IgnoreProperties ignoreProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取session，通过session中SPRING_SECURITY_CONTEXT获取认证上下文
        ServerHttpRequest request = exchange.getRequest();
        String url = request.getURI().getPath();
        if (ignoreProperties.containsPath(url)) {
            return chain.filter(exchange);
        }
        log.info("**********UASFilter start: " + new Date());
        try {
            String authentication = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
            if (StrUtil.isBlank(authentication)) {
                return unauthorized(exchange);
            }
            //检查redis中是否含有该accessCode，如果不存在，则表示该code已经注销了。
            ServerHttpRequest.Builder builder = request.mutate();
            //将token存放到gatewayToken中，后续所有的web请求，如果能从header中获取到该请求，表示请求是从gateway转发的。
            return ReactiveSecurityContextHolder.getContext().flatMap(context -> {
                if(context.getAuthentication() != null && context.getAuthentication() instanceof BearerTokenAuthentication bearerTokenAuthentication){
                    builder.header(SecurityConstants.HEADER_OAUTH_USER_ID, MapUtil.getStr(bearerTokenAuthentication.getTokenAttributes(),GlobalConstants.USER_ID));
                    builder.header(SecurityConstants.HEADER_OAUTH_USER_NAME, MapUtil.getStr(bearerTokenAuthentication.getTokenAttributes(),GlobalConstants.USER_NAME));
                }
                return chain.filter(exchange.mutate().request(builder.build()).build());
            });
        } finally {
            log.debug("**********UASFilter end: " + new Date());
        }
    }


    protected Mono<Void> unauthorized(ServerWebExchange serverWebExchange, String... msg) {
        return Mono.defer(() -> Mono.just(serverWebExchange.getResponse()))
                .flatMap(response -> WebFluxResponseUtils.writeErrorInfo(response, ResultCode.TOKEN_INVALID_OR_EXPIRED));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
